//
//  FPSCameraController.m
//  FilamentDemo
//
//  Created by jerry on 2020/3/7.
//  Copyright © 2020 jerry. All rights reserved.
//

#import "FPSCameraController.h"
#import <Masonry/Masonry.h>
#import <filament/TransformManager.h>
#import "FilamentConvenientHeader.h"
#import "YYWeakProxy.h"
using filament::math::mat4f;
using filament::math::float2;
using filament::math::float3;
using filament::math::float4;
using filament::TransformManager;
typedef NS_ENUM(NSInteger, FPSCameraMoveDir) {
    FPSCameraMoveDirForward = 1,
    FPSCameraMoveDirBack,
    FPSCameraMoveDirLeft,
    FPSCameraMoveDirRight,
    FPSCameraMoveDirUp,
    FPSCameraMoveDirDown,
};

typedef NS_ENUM(NSInteger, FPSCameraRotateDir) {
    FPSCameraRotateDirUp = 1,
    FPSCameraRotateDirDown,
    FPSCameraRotateDirLeft,
    FPSCameraRotateDirRight,
};

typedef NS_ENUM(NSInteger, FPSCameraRotateByAxisType) {
    FPSCameraRotateByAxisX = 1,
    FPSCameraRotateByAxisY,
    FPSCameraRotateByAxisZ
};

float3 normalizedVec2(float3 vec){
    float len = sqrtf(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);
    return float3{vec.x / len, vec.y / len, vec.z / len};
}

@interface FPSCameraController()<UITextFieldDelegate>
@property (nonatomic, assign)filament::Engine* engine;
@property (nonatomic, assign)filament::Camera* camera;
@property (nonatomic, strong)UIView *containerView;


@property (nonatomic, strong)UIView *ctnView;
@property (strong, nonatomic)dispatch_queue_t queue;

@property (nonatomic, assign)BOOL shouldMove;
@property (nonatomic, assign)BOOL shouldRotate;
@property (nonatomic, assign)BOOL shouldRotateByAxis;

@property (nonatomic, assign)FPSCameraMoveDir curMoveDir;
@property (nonatomic, assign)FPSCameraRotateDir curRotateDir;
@property (nonatomic, assign)FPSCameraRotateByAxisType curRotateByAxis;

@property (nonatomic, assign)float moveSpeed; //距离单位/s
@property (nonatomic, assign)float rotateSpeed; //角度/s

@property (weak, nonatomic) IBOutlet UITextField *moveSpeedTf;
@property (weak, nonatomic) IBOutlet UITextField *rotateSpeedTf;

@property (strong, nonatomic) UIView *inputAccessview;
@property (nonatomic, strong) CADisplayLink* displayLink;

@property (nonatomic, assign) mat4f cameraInitTranform;
@property (nonatomic, assign) BOOL isCameraInitTranformHasValue;
@end

@implementation FPSCameraController
 
- (instancetype)initWithEngine:(filament::Engine *)engine
                        camera:(filament::Camera *)camera
                 containerView:(UIView *)containerView
{
    self = [super init];
    if (self) {
        self.engine = engine;
        self.camera = camera;
        self.containerView = containerView;
        [self internalInit];
    }
    return self;
}
- (void)internalInit
{
    _moveSpeed = 5;
    _rotateSpeed = 30;
    _displayLink = [CADisplayLink displayLinkWithTarget:[YYWeakProxy proxyWithTarget:self] selector:@selector(frameCallback)];
    _displayLink.preferredFramesPerSecond = 60;
    [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    _queue = dispatch_queue_create("transform.handle.queue", NULL);
    _ctnView = [[UIView alloc]init];
    UIView *v = [[[NSBundle mainBundle] loadNibNamed:@"FPSCameraController" owner:self options:nil] firstObject];
    [_ctnView addSubview:v];
    [v mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.ctnView);
    }];
    _moveSpeedTf.delegate = self;
    
    _inputAccessview = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 50)];
    UIButton *doneBtn = [UIButton buttonWithType:UIButtonTypeSystem];
    [doneBtn setTitle:@"完成" forState:UIControlStateNormal];
    [doneBtn addTarget:self action:@selector(clickDoneInputBtn) forControlEvents:UIControlEventTouchUpInside];
    [_inputAccessview addSubview:doneBtn];
    [doneBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.width.equalTo(@(100));
        make.height.equalTo(@(50));
        make.centerY.equalTo(self.inputAccessview);
        make.right.equalTo(self.inputAccessview).inset(10);
    }];
    [_inputAccessview mas_makeConstraints:^(MASConstraintMaker *make) {
        make.height.equalTo(@(50));
    }];
    _moveSpeedTf.inputAccessoryView = _inputAccessview;
    _rotateSpeedTf.inputAccessoryView = _inputAccessview;
    
    _rotateSpeedTf.delegate = self;
    _moveSpeedTf.delegate = self;
    
    [self updateSpeedTf];
}
- (void)frameCallback{
    if (_shouldMove) {
        if (!_isCameraInitTranformHasValue) {
            _isCameraInitTranformHasValue = YES;
            _cameraInitTranform = self.camera->getModelMatrix();
        }
        dispatch_async(self.queue, ^{
            float3 forwardVector = self.camera->getForwardVector();
            float3 leftVector = self.camera->getLeftVector();
            float3 upVector = self.camera->getUpVector();
            auto& tcm = self.engine->getTransformManager();
            auto i = tcm.getInstance(self.camera->getEntity());
              
            mat4f curTransform = tcm.getTransform(i);
            
            float moveStep = self.moveSpeed / (float)self.displayLink.preferredFramesPerSecond;
            switch (self.curMoveDir) {
                case FPSCameraMoveDirForward:
                    tcm.setTransform(i, mat4f::translation(normalizedVec2(forwardVector) * moveStep) * curTransform);
                    break;
                case FPSCameraMoveDirBack:
                    tcm.setTransform(i, mat4f::translation(normalizedVec2(forwardVector) * -moveStep) * curTransform);
                    break;
                case FPSCameraMoveDirLeft:
                    tcm.setTransform(i, mat4f::translation(normalizedVec2(leftVector) * -moveStep) * curTransform);
                    break;
                case FPSCameraMoveDirRight:
                    tcm.setTransform(i, mat4f::translation(normalizedVec2(leftVector) * moveStep) * curTransform);
                    break;
                case FPSCameraMoveDirUp:
                    tcm.setTransform(i, mat4f::translation(normalizedVec2(upVector) * moveStep) * curTransform);
                    break;
                case FPSCameraMoveDirDown:
                    tcm.setTransform(i, mat4f::translation(normalizedVec2(upVector) * -moveStep) * curTransform);
                    break;
            }
        });
    }
    if (_shouldRotate) {
        if (!_isCameraInitTranformHasValue) {
            _isCameraInitTranformHasValue = YES;
            _cameraInitTranform = self.camera->getModelMatrix();
        }
        dispatch_async(self.queue, ^{
            float3 upVector = self.camera->getUpVector();
            float3 leftVector = self.camera->getLeftVector();
            float3 position = self.camera->getPosition();
            
            auto& tcm = self.engine->getTransformManager();
            auto i = tcm.getInstance(self.camera->getEntity());
              
            mat4f curTransform = tcm.getTransform(i);
            float rotateStep = (float)self.rotateSpeed / (float)self.displayLink.preferredFramesPerSecond;
            rotateStep = rotateStep / 180.0 * M_PI;
            switch (self.curRotateDir) {
                case FPSCameraRotateDirLeft:
                    tcm.setTransform(i, mat4f::translation(float3{position.x, position.y, position.z})
                                        * mat4f::rotation(rotateStep, normalizedVec2(upVector))
                                        * mat4f::translation(float3{-position.x, -position.y, -position.z})
                                        * curTransform
                                        );
                    break;
                case FPSCameraRotateDirRight:
                    tcm.setTransform(i, mat4f::translation(float3{position.x, position.y, position.z})
                                        * mat4f::rotation(-rotateStep, normalizedVec2(upVector))
                                        * mat4f::translation(float3{-position.x, -position.y, -position.z})
                                        * curTransform
                                        );
                    break;
                case FPSCameraRotateDirUp:
                    tcm.setTransform(i, mat4f::translation(float3{position.x, position.y, position.z})
                                        * mat4f::rotation(rotateStep, normalizedVec2(leftVector))
                                        * mat4f::translation(float3{-position.x, -position.y, -position.z})
                                        * curTransform
                                        );
                    break;
                case FPSCameraRotateDirDown:
                    tcm.setTransform(i, mat4f::translation(float3{position.x, position.y, position.z})
                                        * mat4f::rotation(-rotateStep, normalizedVec2(leftVector))
                                        * mat4f::translation(float3{-position.x, -position.y, -position.z})
                                        * curTransform
                                        );
                    break;
            }
        });
    }
    
    if (_shouldRotateByAxis) {
        if (!_isCameraInitTranformHasValue) {
            _isCameraInitTranformHasValue = YES;
            _cameraInitTranform = self.camera->getModelMatrix();
        }
        dispatch_async(self.queue, ^{
            auto& tcm = self.engine->getTransformManager();
            auto i = tcm.getInstance(self.camera->getEntity());
              
            mat4f curTransform = tcm.getTransform(i);
            float rotateStep = (float)self.rotateSpeed / (float)self.displayLink.preferredFramesPerSecond;
            rotateStep = rotateStep / 180.0 * M_PI;
            switch (self.curRotateByAxis) {
                case FPSCameraRotateByAxisX:
                    tcm.setTransform(i, mat4f::rotation(rotateStep, float3{1,0,0}) * curTransform);
                    break;
                case FPSCameraRotateByAxisY:
                    tcm.setTransform(i, mat4f::rotation(rotateStep, float3{0,1,0}) * curTransform);
                    break;
                case FPSCameraRotateByAxisZ:
                    tcm.setTransform(i, mat4f::rotation(rotateStep, float3{0,0,1}) * curTransform);
                    break;
            }
        });
    }
}

- (void)updateSpeedTf{
    _moveSpeedTf.text = [NSString stringWithFormat:@"%.2lf",_moveSpeed];
    _rotateSpeedTf.text = [NSString stringWithFormat:@"%.2lf",_rotateSpeed];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        UITextPosition *end = [self.moveSpeedTf positionFromPosition:self.moveSpeedTf.endOfDocument offset:0];
        UITextPosition *start = [self.moveSpeedTf positionFromPosition:self.moveSpeedTf.beginningOfDocument offset:0];
        self.moveSpeedTf.selectedTextRange = [self.moveSpeedTf textRangeFromPosition:start toPosition:end];
    });
}
- (void)present{
    [_containerView addSubview:_ctnView];
    [_ctnView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.bottom.equalTo(self.containerView);
        make.height.equalTo(@(267));
    }];
}
- (void)dismiss{
    [self.ctnView removeFromSuperview];
    [self stopMoving];
    [self stopRotating];
}
- (void)startMoving:(FPSCameraMoveDir)moveDir{
    if (_shouldMove) {
        return;
    }
    if (_displayLink.isPaused) {
        _displayLink.paused = NO;
    }
    _curMoveDir = moveDir;
    _shouldMove = YES;
     
}
- (void)stopMoving{
    _shouldMove = NO;
    if (!_shouldRotate && !_shouldRotateByAxis) {
        _displayLink.paused = YES;
    }
}

- (void)startRotating:(FPSCameraRotateDir)rotateDir{
    if (_shouldRotate) {
        return;
    }
    if (_displayLink.isPaused) {
        _displayLink.paused = NO;
    }
    _curRotateDir = rotateDir;
    _shouldRotate = YES;
     
}
- (void)stopRotating{
    _shouldRotate = NO;
    if (!_shouldMove && !_shouldRotateByAxis) {
        _displayLink.paused = YES;
    }
}

- (void)startRotatingByAxis:(FPSCameraRotateByAxisType)rotateAxis{
    if (_shouldRotateByAxis) {
        return;
    }
    if (_displayLink.isPaused) {
        _displayLink.paused = NO;
    }
    _curRotateByAxis = rotateAxis;
    _shouldRotateByAxis = YES;
     
}
- (void)stopRotatingByAxis{
    _shouldRotateByAxis = NO;
    if (!_shouldMove && !_shouldRotate) {
        _displayLink.paused = YES;
    }
}


//MARK: - Target-Action
- (IBAction)clickRestBtn:(id)sender {
    _camera->setModelMatrix(_cameraInitTranform);
}

- (IBAction)moveBtnTouchDown:(UIButton *)sender {
    [self startMoving:(FPSCameraMoveDir)sender.tag];
}

- (IBAction)moveBtnTouchDownCancel:(id)sender {
    [self stopMoving];
}

- (IBAction)rotateBtnTouchDown:(UIButton *)sender {
    [self startRotating:(FPSCameraRotateDir)sender.tag];
}
- (IBAction)rotateBtnTouchDownCancel:(id)sender {
    [self stopRotating];
}

- (IBAction)rotateByAxisBtnTouchDown:(UIButton *)sender {
    [self startRotatingByAxis:(FPSCameraRotateByAxisType)sender.tag];
}
- (IBAction)rotateByAxisTouchDownCancel:(id)sender {
    [self stopRotatingByAxis];
}


- (IBAction)clickCloseBtn:(UIButton *)sender {
    [self dismiss];
}

- (void)clickDoneInputBtn{
    _moveSpeed = [_moveSpeedTf.text floatValue];
    _rotateSpeed = [_rotateSpeedTf.text integerValue];
    [self updateSpeedTf];
    [_moveSpeedTf resignFirstResponder];
    [_rotateSpeedTf resignFirstResponder];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField{
    UITextPosition *end = [textField positionFromPosition:textField.endOfDocument offset:0];
    UITextPosition *start = [textField positionFromPosition:end offset:-textField.text.length];
    textField.selectedTextRange = [textField textRangeFromPosition:start toPosition:end];
}

@end
